package org.artifact.core.plugin.dispatcher;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import co.paralleluniverse.fibers.SuspendExecution;
import org.artifact.core.constant.ArtifactErrorCode;
import org.artifact.core.context.packet.NetworkHeader;
import org.artifact.core.context.packet.NetworkPacket;
import org.artifact.core.context.packet.NetworkPacketExecutor;
import org.artifact.core.context.packet.NetworkPacketInterceptor;
import org.artifact.core.context.session.Session;
import org.artifact.core.lang.AbstractPlugin;
import org.artifact.core.lang.IModule;
import org.artifact.core.plugin.ioc.IocPlugin;
import org.artifact.core.server.ServerConfig;
import org.artifact.core.server.ServerContext;
import org.artifact.core.server.ServerPlugins;
import org.artifact.core.util.LogicUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

public class DispatcherPlugin extends AbstractPlugin {

    static final Log log = LogFactory.get(DispatcherPlugin.class);

    /**
     * 模块集
     */
    private Map<Integer, IModule> modules = new HashMap<>();

    /**
     * 拦截器
     */
    private NetworkPacketInterceptor interceptor;

    /**
     * 执行器
     */
    private NetworkPacketExecutor executor;

    /**
     * 配置
     */
    private DispatcherPluginConfig config;


    public DispatcherPlugin() {
        Map dispatcher = ServerConfig.me().deepGet("plugins.dispatcher");
        this.config = BeanUtil.mapToBean(dispatcher, DispatcherPluginConfig.class, false);

        // 拦截器
        if (StrUtil.isNotBlank(this.config.getInterceptor())) {
            this.interceptor = ReflectUtil.newInstance(this.config.getInterceptor());
        }

        // 执行器
        if (StrUtil.isNotBlank(this.config.getExecutor())) {
            this.executor = ReflectUtil.newInstance(this.config.getExecutor());
        }
    }

    public DispatcherPlugin(DispatcherPluginConfig config) {
        this.config = config;
    }

    public void disp(Session session, NetworkPacket packet){
        try {
            // 消息拦截
            boolean bool = (interceptor != null && interceptor.handler(session, packet));

            if (!bool){
                // 消息执行器
                executor.execute(session, packet,(_session,_packet)->{
                    run(_session, _packet);
                });
            }
        }catch (RuntimeException e) {
            packet.getBody().clear();
            packet.setHeader(NetworkHeader.STATUS, LogicUtil.getErrorCode(e,(error)->{
                log.error(error);
            }));
            session.send(packet);
        }

    }

    private void run(Session session, NetworkPacket packet) throws SuspendExecution, InterruptedException{
        TimeInterval timer = DateUtil.timer();
        try {
            Consumer<NetworkPacket> consumer = module(session, packet);
            if (consumer != null) {
                packet.setHeader(NetworkHeader.STATUS,0);
                consumer.accept(packet);
            }
        } catch (RuntimeException e) {
            packet.getBody().clear();

            packet.setHeader(NetworkHeader.STATUS, LogicUtil.getErrorCode(e,(error)->{
                log.error(error);
            }));

            session.send(packet);
        } finally {
            // 记录执行时间
            int module = packet.getHeader(NetworkHeader.MODULE);
            int method = packet.getHeader(NetworkHeader.METHOD);
            //log.debug("packet {} execute time : {}",packet, timer.interval());
        }
    }

    public Consumer<NetworkPacket> module(Session session, NetworkPacket packet) throws SuspendExecution, InterruptedException{
        IModule module = modules.get(packet.getHeader(NetworkHeader.MODULE));
        LogicUtil.isTrue(ArtifactErrorCode.NOT_FOND_ACTION, module == null);
        return module.disp(session, packet);
    }

    @Override
    public boolean start() {
        try {
            // 扫描包
            scanPackage();

            return true;
        } catch (Exception e) {
            log.error(e);
        }
        return false;
    }

    @Override
    public boolean stop() {
        return true;
    }

    private void scanPackage() {
        String scanPackage = config.getScanPackage();
        if (StrUtil.isEmpty(scanPackage)){
            scanPackage = ServerContext.me().rootPackage();
        }
        Set<Class<?>> clazzs = ClassUtil.scanPackageBySuper(scanPackage, IModule.class);
        for (Class<?> clazz : clazzs) {
            if (ClassUtil.isNormalClass(clazz)) {
                IModule module = ServerPlugins.me().getPlugin(IocPlugin.class).getService(clazz);
                modules.put(module.getModuleId(), module);
            }
        }
    }
}
